//
//  ViewController.m
//  NSOperationDemo
//
//  Created by zxs on 2020/4/30.
//  Copyright © 2020 zxs. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

// 基本使用
@property (weak, nonatomic) IBOutlet UIButton *baseButton;

// 任务组
@property (weak, nonatomic) IBOutlet UIButton *groupButton;

// 任务组
@property (weak, nonatomic) IBOutlet UIButton *safeButton;

// 锁，用于线程安全
@property (strong, nonatomic) NSLock *lock;
// 票的数量
@property (assign, nonatomic) NSInteger ticketNumber;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // 锁；资源锁；用来保护关键资源，这里是ticketNumber
    self.lock = [[NSLock alloc] init];
}

// 基本使用
- (IBAction)baseButtonTouched:(id)sender {
    // 开始之前，修改界面
    self.baseButton.enabled = NO;
    NSLog(@"任务开始前，修改界面...");
    
    // 创建队列，自定义队列默认就是并行的
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 添加耗时操作；其实是匿名operation，直接加block
    [queue addOperationWithBlock:^{
        NSLog(@"耗时任务开始....");
        NSLog(@"%@",[NSThread currentThread]);
        sleep(5);
        NSLog(@"耗时任务结束");
        
        // 切换到主线程，添加界面更新操作；其实是匿名operation，直接加block
        [NSOperationQueue.mainQueue addOperationWithBlock:^{
            self.baseButton.enabled = YES;
            NSLog(@"更新界面");
            NSLog(@"%@",[NSThread currentThread]);
        }];
    }];
}

// 死锁
- (IBAction)deadLockButtonTouched:(id)sender {
    // 设置操作
    NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务A开始....");
        NSLog(@"%@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"任务A结束");
    }];
    
    NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务B开始....");
        NSLog(@"%@",[NSThread currentThread]);
        sleep(1);
        NSLog(@"任务B结束");
    }];
    
    NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务C开始....");
        NSLog(@"%@",[NSThread currentThread]);
        sleep(5);
        NSLog(@"任务C结束");
    }];
    
    // 添加依赖关系；这里B依赖A，C依赖B，A依赖C，循环依赖，造成死锁
    [operationB addDependency:operationA];
    [operationC addDependency:operationB];
    [operationA addDependency:operationC];
    
    // 将操作加入队列；任务会自动启动
    NSOperationQueue *workQueue = [[NSOperationQueue alloc] init];
    [workQueue addOperations:@[operationA, operationB, operationC] waitUntilFinished:NO];
    
    NSLog(@"调度完毕，主线程空闲。。。。");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"等了6s都没完成，估计发生死锁了。手动停掉一个");
        [operationA cancel];
    });
}

// 多读单写
- (IBAction)readWriteButtonTouched:(id)sender {
    // 写之前的读操作
    NSBlockOperation *readOperationBefore = [[NSBlockOperation alloc] init];
    [readOperationBefore addExecutionBlock:^{
        NSLog(@"写之前的读操作1开始....");
        sleep(1);
        NSLog(@"写之前的读操作1结束");
    }];
    [readOperationBefore addExecutionBlock:^{
        NSLog(@"写之前的读操作2开始....");
        sleep(1);
        NSLog(@"写之前的读操作2结束");
    }];
    
    // 写操作
    NSBlockOperation *writeOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"写操作开始....");
        sleep(2);
        NSLog(@"写操作结束");
    }];
    
    // 写之后的读操作
    NSBlockOperation *readOperationAfter = [[NSBlockOperation alloc] init];
    [readOperationAfter addExecutionBlock:^{
        NSLog(@"写之后的读操作1开始....");
        sleep(1);
        NSLog(@"写之后的读操作1结束");
    }];
    [readOperationAfter addExecutionBlock:^{
        NSLog(@"写之后的读操作2开始....");
        sleep(1);
        NSLog(@"写之后的读操作2结束");
    }];
    
    // 添加依赖关系；
    [writeOperation addDependency:readOperationBefore];
    [readOperationAfter addDependency:writeOperation];
    
    // 将操作加入队列；任务会自动启动
    NSOperationQueue *workQueue = [[NSOperationQueue alloc] init];
    [workQueue addOperations:@[readOperationBefore, writeOperation, readOperationAfter] waitUntilFinished:NO];
}

// 任务组
- (IBAction)groupButtonTouched:(id)sender {
    // 网络任务开始前，修改界面
    self.groupButton.enabled = NO;
    NSLog(@"网络组下载任务开始了... ...");
    
    // 将所有的下载子任务都放入一个operation
    NSBlockOperation *workOperation = [[NSBlockOperation alloc] init];
    [workOperation addExecutionBlock:^{
        NSLog(@"任务1开始...");
        sleep(2);
        NSLog(@"任务1结束");
    }];
    
    [workOperation addExecutionBlock:^{
        NSLog(@"任务2开始...");
        sleep(1);
        NSLog(@"任务2结束");
    }];
    
    [workOperation addExecutionBlock:^{
        NSLog(@"任务3开始...");
        sleep(3);
        NSLog(@"任务3结束");
    }];
    
    // 更新界面操作放入一个operation
    NSBlockOperation *mainOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"完成所有子任务，更新界面");
        self.groupButton.enabled = YES;
    }];
    
    // 添加依赖关系
    [mainOperation addDependency:workOperation];
    
    // 将操作加入队列；任务会自动启动
    NSOperationQueue *workQueue = [[NSOperationQueue alloc] init];
    [workQueue addOperation:workOperation];
    [NSOperationQueue.mainQueue addOperation:mainOperation];
}

// 线程安全
- (IBAction)safeButtonTouched:(id)sender {
    // 开始售票
    self.safeButton.enabled = NO;
    self.ticketNumber = 30;
    NSLog(@"售票开始了，一共有%ld张票", (long)self.ticketNumber);
    
    // 所有的售票活动都放入saleOperation
    NSBlockOperation *saleOperation = [[NSBlockOperation alloc] init];
    
    // 售票点1
    [saleOperation addExecutionBlock:^{
        for (int i = 0; i < 4; i++) {
            NSInteger number = 1 + arc4random() % 5;
            [self saleTicketWithName:@"上海站" andNumber:number];
            [NSThread sleepForTimeInterval:1];
        }
    }];
    
    // 售票点2
    [saleOperation addExecutionBlock:^{
        for (int i = 0; i < 3; i++) {
            NSInteger number = 1 + arc4random() % 5;
            [self saleTicketWithName:@"北京站" andNumber:number];
            [NSThread sleepForTimeInterval:1];
        }
    }];
    
    // 售票点3
    [saleOperation addExecutionBlock:^{
        for (int i = 0; i < 3; i++) {
            NSInteger number = 1 + arc4random() % 5;
            [self saleTicketWithName:@"杭州站" andNumber:number];
            [NSThread sleepForTimeInterval:1];
        }
    }];
    
    // 界面更新放入uiOperation
    NSBlockOperation *uiOperation =  [NSBlockOperation blockOperationWithBlock:^{
        self.safeButton.enabled = YES;
        NSLog(@"售票结束，剩余%ld张票", (long)self.ticketNumber);
    }];
    
    // 建立依赖; 先售票，再更新界面
    [uiOperation addDependency:saleOperation];
    
    // 添加队列，并启动
    NSOperationQueue *saleQueue = [[NSOperationQueue alloc] init];
    [saleQueue addOperation:saleOperation];
    [NSOperationQueue.mainQueue addOperation:uiOperation];
}

// 卖票;name:售票窗口名; number:售票数量
- (void)saleTicketWithName:(NSString *)name andNumber:(NSInteger)number {
    // 加锁，保护self.ticketNumber
    [self.lock lock];
    
    if (self.ticketNumber >= number) {
        
        // 售票时间
        [NSThread sleepForTimeInterval:0.5];
        
        self.ticketNumber -= number;
        
        NSLog(@"%@窗口售出%ld张火车票，剩余票数：%ld", name, (long)number, (long)self.ticketNumber);
        
    } else {
        NSLog(@"%@窗口售票%ld张失败,剩余票数：%ld", name, (long)number, (long)self.ticketNumber);
    }
    
    // 解锁
    [self.lock unlock];
}

@end
